/**
 * LinkedLST
 * May 13, 2011
 * ClientView.java - Activity to display list of specimens and bags at a location.
 */

package edu.washington.cs.labspecimentransport;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.http.conn.ConnectTimeoutException;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import edu.washington.cs.labspecimentransport.adapters.BarcodeAdapter;
import edu.washington.cs.labspecimentransport.adapters.SeparatedListAdapter;
import edu.washington.cs.labspecimentransport.data.DataQuery;
import edu.washington.cs.labspecimentransport.dataobjects.Item;
import edu.washington.cs.labspecimentransport.dataobjects.ItemType;
import edu.washington.cs.labspecimentransport.dataobjects.StopInstance;
import edu.washington.cs.labspecimentransport.ops.CommonHandlers;
import edu.washington.cs.labspecimentransport.ops.Conversion;
import edu.washington.cs.labspecimentransport.ops.Keys;

/**
 * A ClientView displays a list of the id of all specimens and bags at a location.
 * 
 * <p>
 * An intent with the following keys are expected:
 * 	String : KEY_LOCATIONNAME - the name of the current location
 * 	int : KEY_LOCATIONID - the current location id
 * 	int : KEY_COURIERID - the courier id
 */
public class ClientView extends Activity {
	
	private DataQuery query;			// DataQuery object for backend communication
	
	private List<Item> itemsAtLocation;	// list of items at a location
	private List<Integer> specimens;	// list of specimen ids at a location
	private List<Integer> containers;	// list of container ids at a location
	
	// map of ids->barcodes for specimens and containers
    private HashMap<Integer, ArrayList<String>> specimenIdAndBarcodes;         
    private HashMap<Integer, ArrayList<String>> containerIdAndBarcodes;
	
	private Bundle infoBundle;			// information stored in this activity's intent
	private int locationId;				// id of the location
	private String locationName;		// name of the location
	private int courierId;				// id of the current courier
	
	private boolean firstExecution;		// flag for whether initial setup is required
	Integer selectedItemId = 0;			// item id of the last selected list index
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.client);  
        firstExecution = true;
        
        // lock the screen in portrait orientation
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        
        setViews();      	// set initial contents for views
        setupButtons();		// set onClick handlers for all buttons
        updateItemsList();	// update the list of items
    }
    
    /**
     * On resume, change screen orientation to portrait in case we
     * are returning from BarcodeView.
     */
    protected void onResume() {
    	super.onResume();
    	setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    /**
     * Define the context menu for list of items.
     */
    public void onCreateContextMenu(ContextMenu menu, View v,
    		ContextMenuInfo menuInfo) {
    	super.onCreateContextMenu(menu, v, menuInfo);
    	menu.setHeaderTitle(R.string.context_header);
    	
    	AdapterView.AdapterContextMenuInfo info =
            (AdapterView.AdapterContextMenuInfo) menuInfo;
    	
    	// get the id of the selected item
    	LinearLayout listItem = (LinearLayout) info.targetView;
		TextView selectedView = (TextView) listItem.findViewById(R.id.item_id);
		selectedItemId = (Integer) selectedView.getTag();
      	
    	MenuInflater inflater = getMenuInflater();
    	inflater.inflate(R.layout.client_single_menucontext, menu);
    }
    
    /**
     * Define functionality for each item in context menu.
     */
    public boolean onContextItemSelected(MenuItem item) {
    	super.onContextItemSelected(item);
    	  switch (item.getItemId()) {
    	  
    	  // open ItemView
    	  case R.id.client_more_info:
    		  new initItemLookup(selectedItemId).execute();
    		  return true;
    	   
    	  // mark item as missing
    	  case R.id.client_mark_missing:
    		// inflate a view for the dialog to use
              LayoutInflater factory = LayoutInflater.from(this);
              View view = factory.inflate(R.layout.dialog_textfield, null);
              
              // set properties for the dialog
              AlertDialog.Builder build = new AlertDialog.Builder(ClientView.this);
              build.setTitle(R.string.client_dialog_missingprompt);
              build.setView(view);

              // add a "Submit" button and define its click handler
              build.setPositiveButton(getString(R.string.dialog_submit), 
            		  new DialogInterface.OnClickListener() {
            	  public void onClick(DialogInterface dialog, int whichButton) 
            	  {
            		  EditText textField = (EditText) 
            		  ((AlertDialog)dialog).findViewById(R.id.text_field);
            		  dialog.dismiss();

            		  Toast toast;

            		  // attempt to mark item as lost
            		  if (!query.markItemAsLost(selectedItemId, 
            				  textField.getText().toString())) {
            			  toast = Toast.makeText(ClientView.this, 
            					  R.string.client_msg_alreadyMissing, Toast.LENGTH_LONG);
            		  } else {
            			  toast = Toast.makeText(ClientView.this, 
            					  R.string.client_msg_markedAsMissing, 
            					  Toast.LENGTH_LONG);
            		  }
            		  toast.show();
            	  }
              });
              // add a "Cancel" button and define its click handler
              build.setNegativeButton(getString(R.string.dialog_submit), 
            		  new DialogInterface.OnClickListener() {
            	  public void onClick(DialogInterface dialog, int whichButton) 
            	  {
            		  dialog.dismiss();
            	  }
              });

              // create and show the dialog
              AlertDialog dialog = build.create();
              dialog.show();
    		  return true;
    	  
    	  default:
    		  return super.onContextItemSelected(item);
    	  }
    }
    
    /**
     * Define the options menu.
     */
    public boolean onCreateOptionsMenu(Menu menu) {
    	menu.add(0, Keys.MENUID_REFRESH, 0, R.string.option_refresh);
    	menu.add(0, Keys.MENUID_HOME, 0, R.string.option_home);
    	menu.add(0, Keys.MENUID_HELP, 0, R.string.option_help);
    	return true;
    }

    /**
     * Define functionality for each item in options menu.
     */
    public boolean onOptionsItemSelected(MenuItem item) {   
        switch(item.getItemId()) {
        case Keys.MENUID_REFRESH:
        	updateItemsList();
        	break;
        
        case Keys.MENUID_HOME:
        	CommonHandlers.returnHome(this);
        	break;
        	
        case Keys.MENUID_HELP:
        	CommonHandlers.startHelp(this);
        	
        default:
        	super.onOptionsItemSelected(item);
        	break;
        }
        
        return true;
    }
    
    /**
     * Extract information from intent
     */
    private void getPassedData() {
    	infoBundle = getIntent().getExtras();
    	locationId = infoBundle.getInt(Keys.KEY_LOCATIONID);
    	courierId = infoBundle.getInt(Keys.KEY_COURIERID);
    	
        // create a new query to retrieve a list of items
        query = DataQuery.getInstance();
    }
    
    /**
     * Refresh the list of items
     */
    private void updateItemsList() {
    	new UpdateItemsAtLocation().execute();
    }
    
    /**
     * Initialize contents of views
     */
    private void setViews() {
    	// set the title header
    	locationName = getIntent().getExtras().getString(Keys.KEY_LOCATIONNAME);
        TextView header = (TextView) findViewById(R.id.headerTitle);
        if (locationName.length() < 14) {
        	header.setText(locationName);
        } else {
        	header.setText(locationName.substring(0, 14) + "...");
        }
    }
    
    /**
     * Sets the adapter for and initializes list of items
     */
    private void displayList() {
        SeparatedListAdapter adapter = new SeparatedListAdapter(this);
        adapter.addSection(getString(R.string.client_ui_specimenHeader) + 
        		" (" + specimens.size() + " " + getString(R.string.client_ui_items) + ")", 
        		new BarcodeAdapter(this, specimenIdAndBarcodes));
        adapter.addSection(getString(R.string.client_ui_containerHeader) + 
        		" (" + containers.size() + " " + getString(R.string.client_ui_items) + ")", 
        		new BarcodeAdapter(this, containerIdAndBarcodes));
        
        ListView listSpecimens = (ListView) findViewById(R.id.listOfItems);
        listSpecimens.setAdapter(adapter);
        
        registerForContextMenu(listSpecimens);
    }
    
    /**
     * Sets onClick handlers for all buttons
     */
    private void setupButtons() {
    	// onClick handler for "Home" button
    	Button h = (Button) findViewById(R.id.home_button);
    	h.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				CommonHandlers.returnHome(ClientView.this);
			}
    	}); 
    	
    	// onClick handler for "Refresh" button
    	Button r = (Button) findViewById(R.id.refresh_button);
    	r.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				updateItemsList();
			}
    	}); 
        
        // onClick handler for "Scan" button
        Button barScanner = (Button) findViewById(R.id.client_scan_button);
        barScanner.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                Intent i = new Intent(getApplicationContext(), BarcodeView.class);

                // store map into the intent that will be passed into BarcodeView
                Bundle intentBundle = new Bundle();
                intentBundle.putSerializable(Keys.KEY_SPECIMENMAP, 
                		specimenIdAndBarcodes);
                intentBundle.putSerializable(Keys.KEY_CONTAINERMAP, 
                		containerIdAndBarcodes);
                intentBundle.putAll(infoBundle);
                i.putExtras(intentBundle);

                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                startActivity(i);
            }
        });
        
        // onClick handler for "Get Directions" button
    	Button directions = (Button) findViewById(R.id.client_directions_button);
    	directions.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				Intent i = new Intent(Intent.ACTION_VIEW, 
						Uri.parse("google.navigation:q=" + locationName));
				startActivity(i);
			}
		});
        
    	// onClick handler for "Next Location" button
        Button nextLocation = (Button) findViewById(R.id.client_next_location);
        nextLocation.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            	new markAsVisited().execute();
            }
        });        
    }
        
    /**
     * Service to get items from the backend database
     */
	private class UpdateItemsAtLocation extends AsyncTask<Void, Void, Void> {
		private ProgressDialog dialog;
		private String message;
		private boolean success;
		private boolean timeout;
		
		// before execution
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			success = true;
			timeout = false;
	    	dialog = new ProgressDialog(ClientView.this);
			dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
			dialog.setMessage(getString(R.string.client_load_updateItemsList_1));
			dialog.setCancelable(true);
			dialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
				public void onCancel(DialogInterface dialog) {
					UpdateItemsAtLocation.this.cancel(true);
					finish();
				}
			});
			dialog.show();
		}
		
		// execution
		@Override
		protected Void doInBackground(Void... v) {
			if (firstExecution) {
				getPassedData();	// get required information from intent
				firstExecution = false;
			}
			try {
				List<Item> allItemsAtLocation = 
					query.getItemsByLocation(courierId, locationId);
				itemsAtLocation = new ArrayList<Item>();

				updateProgressDialog(
						getString(R.string.client_load_updateItemsList_2));
				dialog.incrementProgressBy(20);

				// filter out items that have already been picked up
				for (Item i : allItemsAtLocation) {
					if (!i.getPicked_up()) {
						itemsAtLocation.add(i);
					}
				}

				updateProgressDialog(
						getString(R.string.client_load_updateItemsList_3));
				
				List<Integer> idsAtLocation = 
					Conversion.convertItemsToItemIds(itemsAtLocation);
				List<ItemType> itemTypes = 
					query.getItemTypesByIds(idsAtLocation);
				
				dialog.incrementProgressBy(35);

				updateProgressDialog(
						getString(R.string.client_load_updateItemsList_4));
				dialog.incrementProgressBy(5);
				
				specimens = new ArrayList<Integer>();
				containers = new ArrayList<Integer>();

				for (int i = 0; i < itemTypes.size(); i++) {
					ItemType curr = itemTypes.get(i);
					if (curr.getIs_container()) {
						containers.add(idsAtLocation.get(i));
					} else {
						specimens.add(idsAtLocation.get(i));
					}
				}

				updateProgressDialog(
						getString(R.string.client_load_updateItemsList_5));

				// convert list of items to a map of ids to barcodes that 
				// may be passed through intents
				specimenIdAndBarcodes = 
					Conversion.convertItemIdListToBarcodeList(specimens);   
				dialog.incrementProgressBy(20);
				containerIdAndBarcodes = 
					Conversion.convertItemIdListToBarcodeList(containers);
				dialog.incrementProgressBy(20);
			} catch (NullPointerException e) {
				success = false;
			} catch (ConnectTimeoutException e) {
				timeout = true;
			}
			return null;
		}

		// after execution
		@Override
		protected void onPostExecute(Void unused) {
			dialog.dismiss();
			if (success) {
				displayList();
			} else if (timeout) {
				Toast toast = Toast.makeText(ClientView.this, 
						R.string.error_timeout, Toast.LENGTH_LONG);
				toast.show();
			} else {
				Toast toast = Toast.makeText(ClientView.this, 
						R.string.error_default, Toast.LENGTH_LONG);
				toast.show();
			}
		}
		
		private void updateProgressDialog(String msg) {
			message = msg;
			runOnUiThread(changeMessage);
		}
		
		private Runnable changeMessage = new Runnable() {
		    public void run() {
		        dialog.setMessage(message);
		    }
		};
	}
	
	/**
	 * Service to look up an item's information.
	 */
	private class initItemLookup extends AsyncTask<Void, Void, Void> {
		
		private ProgressDialog dialog;
		private int item_id;
		private Intent intent;
		
		public initItemLookup(int item_id) {
			this.item_id = item_id;
		}
		
		// before execution
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			dialog = ProgressDialog.show(ClientView.this, "", 
					getString(R.string.client_load_itemLookup), true, true,
					new DialogInterface.OnCancelListener(){
						public void onCancel(DialogInterface dialog) {
							initItemLookup.this.cancel(true);
							finish();
						}
			});
		}
		
		// execution
		@Override
		protected Void doInBackground(Void... params) {
			intent = new Intent(ClientView.this, ItemView.class);
			Bundle newBundle = new Bundle();
			newBundle.putAll(infoBundle);
			newBundle.putInt(Keys.KEY_ITEMID, item_id);
			
			if (containerIdAndBarcodes.containsKey(item_id)) {
				newBundle.putStringArrayList(Keys.KEY_BARCODE, 
						containerIdAndBarcodes.get(item_id));
				newBundle.putBoolean(Keys.KEY_ISCONTAINER, true);
			} else {
				newBundle.putStringArrayList(Keys.KEY_BARCODE, 
						specimenIdAndBarcodes.get(item_id));
				newBundle.putBoolean(Keys.KEY_ISCONTAINER, false);
			}
			
			intent.putExtras(newBundle);
			return null;
		}
		
		// after execution
		@Override
		protected void onPostExecute(Void unused) {
			startActivity(intent);
			dialog.dismiss();
		}
	}

	/**
	 * Service to mark a location as visited. Not cancelable.
	 */
	private class markAsVisited extends AsyncTask<Void, Void, Void> {
		private ProgressDialog dialog;
		private boolean success;
		private boolean timeout;
		
		// before execution
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			success = true;
			timeout = false;
			dialog = ProgressDialog.show(ClientView.this, "", 
					getString(R.string.client_load_markVisited), true);
		}
		
		// execution
		@Override
		protected Void doInBackground(Void... params) {
			try {
	           	List<StopInstance> stopinstances = 
	           		query.getStopInstancesByCourierId(courierId);
            	for (StopInstance s : stopinstances) {
            		if (s.getLocation_id() == locationId) {
            			Log.d("DERP", "Marking " + locationId + " as visited");
            			query.markLocationAsVisited(s);
            			break;
            		}
            	}
			} catch (NullPointerException e) {
				success = false;
			} catch (ConnectTimeoutException e) {
				timeout = true;
			}
			
			return null;
		}
		
		// after execution
		@Override
		protected void onPostExecute(Void unused) {
			dialog.dismiss();
			
			if (success) {
				// return to LocationsView on success
				Intent myIntent = new Intent(ClientView.this, 
						LocationsView.class);
                myIntent.putExtras(infoBundle);
                
                query.flushEntireCache();
                
                startActivity(myIntent);
			} else if (timeout) {
				Toast toast = Toast.makeText(ClientView.this, 
						R.string.error_timeout, Toast.LENGTH_LONG);
				toast.show();
			} else {
				Toast toast = Toast.makeText(ClientView.this, 
						R.string.error_send, Toast.LENGTH_LONG);
				toast.show();
			}
		}
	}
}
